<?php
/**
 * Contains DB_Connection class
 *
 * @package tangra_lib
 * @subpackage registry
 */

/**
 *
 */
require_once(TANGRA_MAIN_DIR.'db/db_connection.class.php');
/**
 *
 */
require_once(TANGRA_MAIN_DIR.'exceptions/tangra_module_exception.class.php');

/**
 *
 */
require_once(TANGRA_MAIN_DIR.'/registry/registry.class.php');
/**
 *
 */
require_once('registry_key_db.class.php');
/**
 *
 */
require_once(TANGRA_MAIN_DIR.'/registry/te_registry_key_already_exists.class.php');
/**
 *
 */
require_once(TANGRA_MAIN_DIR.'/registry/te_registry_key_not_exists.class.php');


/**
 * Regisrty_DB provides Registry with storage of data in DB
 *
 * @package tangra_lib
 * @subpackage registry
 */
class Registry_DB extends Registry {
	/**
	 * Holds DB_Connection instance
	 *
	 * @var DB_Connection
	 * @internal
	 */
	private $dbc;
	/**
	 * Holds table name for the registry db table
	 *
	 * @var string
	 */
	private $table_name;


	/**
	 * Constructor
	 *
	 * @param DB_Connection $dbc
	 * @param string $table_name Table name for the registry db table
	 */
	function __construct(DB_Connection $dbc, $table_name = 'registry') {
		$this->dbc = $dbc;

		$this->set_table_name($table_name);
	}


	/**
	 * Returns db connection
	 *
	 * @return DB_Connection
	 */
	public function get_dbc() {
		return $this->dbc;
	}


	/**
	 * Sets table name for the registry db table
	 *
	 * @param string $table_name
	 */
	public function set_table_name($table_name) {
		if (ereg("^[a-zA-Z]{1}[a-zA-Z0-9_]{1,100}$", $table_name)) {
			$this->table_name = $table_name;
		} else {
			throw new Tangra_Module_Exception('Invalid table name: '.$table_name);
		}
	}


	/**
	 * Returns table name for the registry db table
	 *
	 * @return string
	 */
	public function get_table_name() {
		return $this->table_name;
	}


	/**
	 * Creates key with full path given
	 *
	 * @param string $key_full_path Full path of the key (Please note that path must NOT end with slash (/))
	 * @param integer $type Type of the key (@see Registry_Key_DB for constants)
	 * @param mixed $value Value of the key
	 * @return integer On success returns the ID of the key. On failure - return false
	 */
	public function create_key_w_path($key_full_path, $type, $value, $forgiving = true) {
		$ret = false;

		$dbc = $this->get_dbc();
		if (!Registry_Key_DB::key_exists($dbc, $key_full_path)) {
			$path_arr = Registry_Key_DB::decompose_key_path($key_full_path);
			$key_name = $path_arr[count($path_arr) - 1];
			if (count($path_arr) > 1) {
				unset($path_arr[count($path_arr) - 1]); //removing key_name
				$path_parent = '/'.implode('/', $path_arr);

				$parent_id = Registry_Key_DB::key_exists($dbc, $path_parent);
			} else {
				$parent_id = 0;
			}
			if ($parent_id !== false) {
				$key = new Registry_Key_DB($this->get_table_name());
				$key->set_key_name($key_name);
				$key->set_type($type);
				$key->set_value($value);
				$key->set_parent($parent_id);

				$this->save_key($dbc, $key);
				$ret = $key;
			} else {
				throw new TE_Registry_Key_Not_Exists($path_parent);
			}
		} else {
			if (!$forgiving) {
				throw new TE_Registry_Key_Already_Exists($key_full_path);
			}
		}

		return $ret;
	}


	/**
	 * Creates key with parent ID given
	 *
	 * @param integer $parent_id ID of the parent
	 * @param string $key_name Name of the key
	 * @param integer $type Type of the key (@see Registry_Key_DB for constants)
	 * @param mixed $value Value of the key
	 */
	public function create_key_with_parent_id($parent_id, $key_name, $type, $value) {
		$dbc = $this->get_dbc();
		$key = new Registry_Key_DB($this->get_table_name());

		if (!$key->load_by_parent_and_key_name($dbc, $key_name, $parent_id)) {
			if ($parent_id != 0 && !$key->load_by_id($dbc, $parent_id)) {
				throw new TE_Registry_Key_Not_Exists('id: '.$parent_id);
			}

			$key = new Registry_Key_DB($this->get_table_name());
			$key->set_key_name($key_name);
			$key->set_type($type);
			$key->set_value($value);
			$key->set_parent($parent_id);
			$this->save_key($dbc, $key);
		} else {
			throw new TE_Registry_Key_Already_Exists($parent_id.'/'.$key_name);
		}
	}


	/**
	 * Returns value of the key
	 *
	 * @param string $key_full_path Full path of the key
	 * @param boolean $forgiving If false and key does not exist will throw excepion. If $forgiving = true and key not exists will return false
	 * @throws TE_Registry_Key_Not_Exists
	 * @return mixed
	 */
	public function get_key_value($key_full_path, $forgiving = false) {
		$key = new Registry_Key_DB($this->get_table_name());
		$dbc =  $this->get_dbc();

		if ($key->load_by_full_path($dbc, $key_full_path)) {
			$ret = $key->get_value();
		} else {
			if ($forgiving) {
				$ret = false;
			} else {
				throw new TE_Registry_Key_Not_Exists($key_full_path);
			}
		}

		return $ret;
	}


	/**
	 * Sets value of a key.
	 *
	 * @param string $key_full_path Full path of the key
	 * @param mixed $value New value
	 */
	public function set_key_value($key_full_path, $value) {
		$key = new Registry_Key_DB($this->get_table_name());
		$dbc =  $this->get_dbc();

		if ($key->load_by_full_path($dbc, $key_full_path)) {
			$key->set_value($value);
			$this->save_key($dbc, $key);
		} else {
			throw new TE_Registry_Key_Not_Exists($key_full_path);
		}
	}


	/**
	 * Saves key in DB
	 *
	 * @param DB_Connection $dbc
	 * @param Registry_Key_DB $key
	 * @internal
	 */
	protected function save_key(DB_Connection $dbc, Registry_Key_DB $key) {
		$dbc->start_trans();
		try {
			$key->save($dbc);
			$dbc->complete_trans();
		} catch (Exception $e){
			$dbc->fail_trans();
			$dbc->complete_trans();
			throw $e;
		}
	}

}